﻿using StackExchange.Redis;
using System;
using System.Collections.Generic;

namespace JESAI.DistributedLock.Redis;

/// <summary>
/// 注意：我们不允许在这里禁用自动扩展，因为这会导致陷阱
/// 人们可能会放弃句柄，然后由于GC而关闭它。
/// 请参阅此处的讨论：https://github.com/madelson/DistributedLock/issues/130。
/// </summary>
public sealed class RedisDistributedSynchronizationProvider : IDistributedLockProvider, IDistributedReaderWriterLockProvider, IDistributedSemaphoreProvider
{
    private readonly IReadOnlyList<IDatabase> _databases;
    private readonly Action<RedisDistributedSynchronizationOptionsBuilder>? _options;

    /// <summary>
    /// 构造一个 <see cref="RedisDistributedSynchronizationProvider"/>，它连接到提供的 <paramref name="database"/>
    /// 并使用提供的 <paramref name="options"/>。
    /// </summary>
    public RedisDistributedSynchronizationProvider(IDatabase database, Action<RedisDistributedSynchronizationOptionsBuilder>? options = null)
        : this(new[] { database ?? throw new ArgumentNullException(nameof(database)) }, options)
    {
    }

    /// <summary>
    /// 构造一个 <see cref="RedisDistributedSynchronizationProvider"/>，它连接到提供的 <paramref name="databases"/>
    /// 并使用提供的 <paramref name="options"/>。
    /// 
    /// 注意，如果提供了多个 <see cref="IDatabase"/>，<see cref="CreateSemaphore(RedisKey, int)"/> 将只使用第一个
    /// <see cref="IDatabase"/>。
    /// </summary>
    public RedisDistributedSynchronizationProvider(IEnumerable<IDatabase> databases, Action<RedisDistributedSynchronizationOptionsBuilder>? options = null)
    {
        this._databases = RedisDistributedLock.ValidateDatabases(databases);
        this._options = options;
    }

    /// <summary>
    /// 构造一个 <see cref="RedisDistributedSynchronizationProvider"/>，它连接到提供的 <paramref name="databases"/>
    /// 并使用提供的 <paramref name="options"/>。
    /// 
    /// 注意，如果提供了多个 <see cref="IDatabase"/>，<see cref="CreateSemaphore(RedisKey, int)"/> 将只使用第一个
    /// <see cref="IDatabase"/>。
    /// </summary>

    public RedisDistributedLock CreateLock(RedisKey key) => new(key, this._databases, this._options);

    IDistributedLock IDistributedLockProvider.CreateLock(string name) => this.CreateLock(name);

    /// <summary>
    /// 使用给定的 <paramref name="name"/> 创建一个 <see cref="RedisDistributedReaderWriterLock"/>。
    /// </summary>
    public RedisDistributedReaderWriterLock CreateReaderWriterLock(string name) =>
        new(name, this._databases, this._options);

    IDistributedReaderWriterLock IDistributedReaderWriterLockProvider.CreateReaderWriterLock(string name) =>
        this.CreateReaderWriterLock(name);

    /// <summary>
    /// 使用提供的 <paramref name="key"/> 和 <paramref name="maxCount"/> 创建一个 <see cref="RedisDistributedSemaphore"/>。
    /// </summary>
    public RedisDistributedSemaphore CreateSemaphore(RedisKey key, int maxCount) => new(key, maxCount, this._databases[0], this._options);

    IDistributedSemaphore IDistributedSemaphoreProvider.CreateSemaphore(string name, int maxCount) =>
        this.CreateSemaphore(name, maxCount);
}
